<!--Copyright to Shen Huang, you can reach me out at shenhuang@live.ca-->

<!DOCTYPE html>
<meta name = "viewport" content = "width = device-width, initial-scale = 1.0">
<html>
	<head>
		<title>🍪 COOKIES 🍪</title>	
		<style>
		@keyframes bgcolor {
			0% {
				background-color : #000f04;
			}
			100% {
				background-color : #010112;
			}
		}
		body {
			background-color: #010112;
			-webkit-animation : bgcolor 5s ease-out;
			animation : bgcolor 5s ease-out;
		}
		html, body {
			max-width: 100%;
			overflow-x: hidden;
			overflow-y: hidden;
		}
		.dummy {
			z-index : 100;
			position : absolute;
			line-height : 100%;
		}
		.player {
			z-index : 100;
			visibility : hidden;
			position : absolute;
			line-height : 100%;
		}
		.friend {
			z-index : 100;
			visibility : hidden;
			position : absolute;
			line-height : 100%;
		}
		.cookie {
			z-index : 80;
			visibility : hidden;
			position : absolute;
			line-height : 100%;
		}
		.energy {
			z-index : 999;
			position : absolute;
			height : 35px;
			opacity : 0.5;
			background : #abcdef;
			top : 0%;
			left : 0%;
		}
		.alert {
			z-index : 999;
			position : absolute;
			width : 100%;
			font-size : 100px;
			color : white;
			font-family : Impact;
			text-align : center;
			top : 40%;
		}
		</style>
	</head>
	<body>

	</body>
	<script>

		const returnURL = "../index.html";

		const fieldSizeX = 12000;
		const fieldSizeY = 8000;
		const maxCookieCount = 150;
		const minCookieWeight = 9000;
		
		const startSize = 16;
		const startWeight = 10000;
		const limitWeight = 8000000;
		const displaySize = Math.sqrt(startWeight);

		var cursor_position = {x: 0, y: 0};
		var cursor_position_first;
		var mouse_down = false;

		window.onmouseup = function(event){
			updateCursorPosition(event);
			mouse_down = false;
			updateSound();
		};

		window.onmousedown = function(event){
			updateCursorPosition(event);
			mouse_down = true;
		};

		window.onmousemove = function(event){
			updateCursorPosition(event);
		};

		function updateCursorPosition(mouseEvent)
		{
			cursor_position.x = mouseEvent.x;
			cursor_position.y = mouseEvent.y;
		}

		document.addEventListener('keypress', keypressHandler);

		function keypressHandler(e)
		{
			switch(e.key)
			{
				case '+':
					camera.scaleFinal += camera.scaleChunk;	
					break;
				case '-':
					if(camera.scaleFinal > 1)
						camera.scaleFinal -= camera.scaleChunk;	
					break;
			}
		}

		var cookie_pickup = new Audio("cookie_pickup.mp3");

		function updateSound()
		{
			if(cookie_pickup.muted == true)
			{
				cookie_pickup.muted = false;
			}
		}

		var dummy;
		var camera = createCamera(fieldSizeX / 2, fieldSizeY / 2, 1);
		var player = createPlayer(fieldSizeX / 2, fieldSizeY / 2, startWeight);

		function createCamera(x, y, scale)
		{
			camera = [];
			camera.position = [];
			camera.position.x = x;
			camera.position.y = y;
			camera.scale = scale;
			camera.scaleFinal = 1;
			camera.scaleSpeed = 0.025;
			camera.scaleChunk = 1.5;
			camera.scaleRatio = 20;
			camera.moveSpeed = 0.5;
			return camera;
		}

		function createPlayer(x, y, weight)
		{
			var player = document.createElement("DIV");
			player.emoji = []
			player.emoji.regular = "👀";
			player.emoji.excited = "👀";
			player.textContent = player.emoji.regular;
			player.setAttribute('class', 'player');
			document.body.appendChild(player);
			player.weightBase = weight;
			player.weight = player.weightBase;
			player.growth = 1.0;
			player.snapBoundary = 100;
			var size = Math.sqrt(player.weight);
			player.setAttribute('width', size);
			player.setAttribute('height', size);
			player.displaySize = size;
			player.style.fontSize = player.displaySize + "px";
			player.position = [];
			player.position.x = x;
			player.position.y = y;
			player.sprintFactor = 2.5;
			player.speedScale = 0.25;
			player.speedBase = 0.45;
			player.speed = player.speedBase;
			player.energy = 10000000;
			player.energyReg = 0.01;
			player.energyCon = 0.3;
			player.energyCov = 500;
			player.energyBase = 100000000;
			player.energyBar = document.createElement("DIV");
			player.energyBar.setAttribute('class', 'energy');
			player.style.width = player.energy / player.energyBase + "%";
			document.body.appendChild(player.energyBar);
			return player;
		}

		var friends = [];

		createFriend(500, 500, 10000, "STATE_MACHINE");
		createFriend(fieldSizeX - 500, 500, 10000, "STATE_MACHINE");
		createFriend(500, fieldSizeY - 500, 10000, "STATE_MACHINE");
		createFriend(fieldSizeX - 500, fieldSizeY - 500, 10000, "STATE_MACHINE");

		function createFriend(x, y, weight, algorithm)
		{
			var friend = document.createElement("DIV");
			friend.algorithm = algorithm;
			friend.emoji = [];
			friend.emoji.regular = "😋";
			friend.emoji.excited = "😍";
			friend.emoji.worried = "😥";
			friend.emoji.bullied = "😣";
			friend.emoji.anxious = "😖";
			friend.emoji.kissing = "😚";
			friend.emoji.airkiss = "😘";
			friend.textContent = friend.emoji.regular;
			friend.setAttribute('class', 'friend');
			document.body.appendChild(friend);
			friend.weightBase = weight;
			friend.weight = friend.weightBase;
			friend.growth = 0.9;
			friend.snapBoundary = 1.3;
			var size = Math.sqrt(friend.weight);
			friend.setAttribute('width', size);
			friend.setAttribute('height', size);
			friend.displaySize = size;
			friend.style.fontSize = friend.displaySize + "px";
			friend.position = [];
			friend.position.x = x;
			friend.position.y = y;
			friend.sprintFactor = 3.5;
			friend.speedScale = 0.25;
			friend.speedBase = 0.35;
			friend.speed = friend.speedBase;
			friend.onSprint = false;
			friend.energy = 0;
			friend.energyReg = 0.1;
			friend.energyCon = 0.3;
			friend.energyBase = 3000;
			friend.sight = [];
			friend.sight.oRadius = 2000 * 2000;
			friend.sight.wRadius = 1500 * 1500;
			friend.sight.rRadius = 1000 * 1000;
			friend.sight.iRadius = 500 * 500;
			friend.panicBase = 1000;
			friend.panicTime = 0;
			friend.sight.predators = [];
			friend.sight.preys = [];
			friend.weightRatio = 1.1;
			friend.weightTreshold = startWeight;
			friend.nextCookie;
			friend.wanderAngle;
			friends.push(friend);
			return friend;
		}

		var cookies = [];

		function createCookie(x, y, weight)
		{
			var cookie = document.createElement("DIV");
			cookie.textContent = "🍪";
			cookie.setAttribute('class', 'cookie');
			document.body.appendChild(cookie);
			cookie.weight = weight;
			var size = Math.sqrt(cookie.weight);
			cookie.setAttribute('width', size);
			cookie.setAttribute('height', size);
			cookie.displaySize = size;
			cookie.style.fontSize = cookie.displaySize + "px";
			cookie.position = [];
			cookie.position.x = x;
			cookie.position.y = y;
			cookies.push(cookie)
			return cookie;
		}

		const loadTimeInitial = 1000;
		var loadTime = loadTimeInitial;
		var before = Date.now();
		var gc = setInterval(generateCookies, 100);
		var xHalf = window.innerWidth / 2;
		var yHalf = window.innerHeight / 2;
		fr = setInterval(updateFrame, 25);

		function updateFrame()
		{
			var current = Date.now();
			var deltaTime = current - before;
			before = current;
			updateCamera(deltaTime);
			for(i in cookies)
			{
				updateCookie(deltaTime, cookies[i]);
			}
			for(i in friends)
			{
				updateFriend(deltaTime, friends[i]);
			}
			updatePlayer(deltaTime);
		}

		function updateCamera(deltaTime)
		{
			var sDiff = camera.scaleFinal - camera.scale;
			camera.scale += absoluteMin(Math.sign(sDiff) * camera.scaleSpeed * deltaTime, sDiff);
			var xDiff = player.position.x - camera.position.x;
			var yDiff = player.position.y - camera.position.y;
			var xHalf = window.innerWidth / 2;
			var yHalf = window.innerHeight / 2;
			var angle = Math.atan2(yDiff, xDiff);
			camera.position.x += camera.moveSpeed * Math.sign(xDiff) * Math.sqrt(Math.abs(xDiff));
			camera.position.y += camera.moveSpeed * Math.sign(yDiff) * Math.sqrt(Math.abs(yDiff));
			if(camera.position.x < xHalf * camera.scale)
			{
				if(camera.position.x > fieldSizeX - xHalf * camera.scale)
				{
					camera.position.x = fieldSizeX / 2;
				}
				else
				{
					camera.position.x = xHalf * camera.scale;
				}
			}
			else if(camera.position.x > fieldSizeX - xHalf * camera.scale)
			{
				camera.position.x = fieldSizeX - xHalf * camera.scale;
			}
			if(camera.position.y < yHalf * camera.scale)
			{
				if(camera.position.y > fieldSizeY - yHalf * camera.scale)
				{
					camera.position.y = fieldSizeY / 2;
				}
				else
				{
					camera.position.y = yHalf * camera.scale;
				}
			}
			else if(camera.position.y > fieldSizeY - yHalf * camera.scale)
			{
				camera.position.y = fieldSizeY - yHalf * camera.scale;
			}
		}

		function showOnCamera(object)
		{
			var size = object.displaySize / camera.scale;
			var xHalf = window.innerWidth / 2;
			var yHalf = window.innerHeight / 2;
			var xScreen = (object.position.x - camera.position.x) / camera.scale + xHalf;
			var yScreen = (object.position.y - camera.position.y) / camera.scale + yHalf;
			if(xScreen > -1 * size && xScreen < window.innerWidth + size && yScreen > -1 * size && yScreen < window.innerHeight + size)
			{

				object.style.left = (xScreen - size / 2) + "px";
				object.style.top = yScreen + "px";
				object.style.marginLeft = (-1 * size) + "px";
				object.style.marginTop = (-1 * size / 2) + "px";
				object.style.textIndent = size + "px";
				object.style.fontSize = size + "px";
				object.style.visibility = "visible";
				return {
					x : xScreen,
					y : yScreen,
				}

			}
			object.style.visibility = "hidden";
		}

		function updatePlayer(deltaTime)
		{
			var speedFactor = 1;
			if(mouse_down)
			{
				if(player.energy > 0)
				{
					player.textContent = player.emoji.excited;
					speedFactor = player.sprintFactor;
					player.energy -= player.energyCon * deltaTime;
				}
				else
				{
					player.textContent = player.emoji.regular;
					player.energy = 0;
				}
			}
			else
			{
				player.textContent = player.emoji.regular;
				if(player.energy < player.energyBase)
				{
					player.energy += player.energyReg * deltaTime;
				}
				else
				{
					player.energy = player.energyBase;
				}
			}
			player.energyBar.style.width = player.energy / player.energyBase * 100 + "%";
			var screen_position = showOnCamera(player);
			player.speed = player.speedBase / (1 + player.speedScale * Math.log(player.weight / player.weightBase));
			var xDiff = cursor_position.x - screen_position.x;
			var yDiff = cursor_position.y - screen_position.y;
			var angle = Math.atan2(yDiff, xDiff);
			player.position.x += absoluteMin(player.speed * Math.cos(angle) * deltaTime * speedFactor, xDiff);
			player.position.y += absoluteMin(player.speed * Math.sin(angle) * deltaTime * speedFactor, yDiff);
			if(player.position.x < player.displaySize / 2)
				player.position.x = player.displaySize / 2;
			if(player.position.x > fieldSizeX - player.displaySize / 2)
				player.position.x = fieldSizeX - player.displaySize / 2;
			if(player.position.y < player.displaySize / 2)
				player.position.y = player.displaySize / 2;
			if(player.position.y > fieldSizeY - player.displaySize / 2)
				player.position.y = fieldSizeY - player.displaySize / 2;
		}

		function updateCookie(deltaTime, cookie)
		{
			if(showOnCamera(cookie))
			{

			}
			for(i in friends)
			{
				friend = friends[i];
				xDiff = friend.position.x - cookie.position.x;
				yDiff = friend.position.y - cookie.position.y;
				sDiff = friend.displaySize - cookie.displaySize;
				if(cookie.weight > 0 && squaredEuclidean(xDiff, yDiff) <= sDiff * sDiff / 4 + friend.snapBoundary)
				{
					if(friend.displaySize >= cookie.displaySize)
					{
						friend.weight += cookie.weight * friend.growth;
						friend.displaySize = Math.sqrt(friend.weight);
						cookie.weight = -1;
					}
				}
				else
				{
					//Should Friends die to Cookies?
				}
			}
			if(cookie.weight < 0 && cookie.parentNode != null)
			{
				var index = cookies.indexOf(cookie);
				cookie.parentNode.removeChild(cookie);
				cookies.splice(index, 1);
			}
		}

		const fitTimeOut = 5;

		function generateCookies()
		{

			/* Rework cookie generation, maybe related to max agent size. */

			if(cookies.length < Math.floor(maxCookieCount * (limitWeight - player.weight) / limitWeight))
			{
				var weight = minCookieWeight + (Math.sqrt(player.weight / startWeight) * startWeight - minCookieWeight) * Math.pow(Math.random(), 3);
				var size = Math.sqrt(weight);
				var validPosition = false;
				var count = 0;
				while(!validPosition && count < fitTimeOut)
				{
					var x = size / 2 + (fieldSizeX - size) * Math.random();
					var y = size / 2 + (fieldSizeY - size) * Math.random();
					validPosition = true;
					var xDiff = player.position.x - x;
					var yDiff = player.position.y - y;
					var sDiff = player.displaySize + player.snapBoundary + size;
					if(squaredEuclidean(xDiff, yDiff) < sDiff * sDiff / 4)
						validPosition = false;
					for(i in cookies)
					{
						cookie = cookies[i];
						xDiff = cookie.position.x - x;
						yDiff = cookie.position.y - y;
						sDiff = cookie.displaySize + size;
						if(squaredEuclidean(xDiff, yDiff) < sDiff * sDiff / 4)
							validPosition = false;
					}
				}
				if(count < fitTimeOut)
					createCookie(x, y, weight);
			}
		}

		function updateFriend(deltaTime, friend)
		{
			var sprintRequired, xDiff, yDiff, sDiff;
			switch(friend.algorithm)
			{
				case "STATE_MACHINE":
					sprintRequired = false;
					if(friend.panicTime >= 0)
					{
						friend.panicTime -= deltaTime;
					}
					else
					{
						friend.sight.predators = [];
					}
					friend.sight.preys = [];
					for(i in friends)
					{
						var otherFriend = friends[i];
						if(otherFriend != friend)
						{
							xDiff = friend.position.x - otherFriend.position.x;
							yDiff = friend.position.y - otherFriend.position.y;
							sDiff = friend.displaySize - otherFriend.displaySize;
							if(squaredEuclidean(xDiff, yDiff) < friend.sight.wRadius + sDiff * sDiff / 4)
							{
								if(otherFriend.weight > friend.weight)
								{
									friend.panicTime = friend.panicBase;
									if(!friend.sight.predators.includes(otherFriend))
										friend.sight.predators.push(otherFriend);
								}
								else if(otherFriend.weight < friend.weight * friend.weightRatio + friend.weightTreshold && otherFriend.weight > 0)
								{
									friend.sight.preys.push(otherFriend);
								}
							}
						}
					}
					if(friend.sight.predators.length > 0)
					{
						friend.nextCookie = null;
						friend.textContent = friend.emoji.worried;
						var xPanic = 0;
						var yPanic = 0;
						for(i in friend.sight.predators)
						{
							var predator = friend.sight.predators[i];
							xDiff = friend.position.x - predator.position.x;
							yDiff = friend.position.y - predator.position.y;
							sDiff = predator.displaySize - friend.displaySize + predator.snapBoundary;
							var distance = squaredEuclidean(xDiff, yDiff);
							if(distance < friend.sight.rRadius + sDiff * sDiff / 4)
							{
								if(distance < friend.sight.iRadius + sDiff * sDiff / 4)
								{
									sprintRequired = true;
									friend.textContent = friend.emoji.anxious;
								}
								else
								{
									friend.textContent = friend.emoji.bullied;
								}
								var angle = Math.atan2(yDiff, xDiff);
								xPanic += Math.cos(angle) / distance;
								yPanic += Math.sin(angle) / distance;
							}
						}
						friend.wanderAngle = Math.atan2(yPanic, xPanic);
					}
					if(friend.sight.preys.length > 0)
					{
						friend.nextCookie = null;
						var largestPrey = friend.sight.preys[0];
						for(i = 1; i < friend.sight.preys.length; i++)
						{
							if(largestPrey.weight < friend.sight.preys[i].weight)
								largestPrey = friend.sight.preys[i];
						}
						xDiff = largestPrey.position.x - friend.position.x;
						yDiff = largestPrey.position.y - friend.position.y;
						sDiff = friend.displaySize - largestPrey.displaySize + friend.snapBoundary;
						var distance = squaredEuclidean(xDiff, yDiff);
						if(distance < friend.sight.rRadius + sDiff * sDiff / 4)
						{
							if(distance < friend.sight.iRadius + sDiff * sDiff / 4)
							{
								sprintRequired = true;
								friend.textContent = friend.emoji.airkiss;
							}
							else
							{
								friend.textContent = friend.emoji.kissing;
							}
							friend.wanderAngle = Math.atan2(yDiff, xDiff);
						}
					}
					if(friend.sight.predators == 0 && friend.sight.preys == 0)
					{
						friend.textContent = friend.emoji.regular;
						if(friend.nextCookie == null || friend.nextCookie.weight < 0)
						{
							var oCookie, iCookie;
							for(i in cookies)
							{
								cookie = cookies[i];
								if(cookie.weight > 0)
								{
									xDiff = cookie.position.x - friend.position.x;
									yDiff = cookie.position.y - friend.position.y;
									sDiff = friend.displaySize - cookie.displaySize;
									if(squaredEuclidean(xDiff, yDiff) < friend.sight.oRadius + sDiff * sDiff / 4)
									{
										if(cookie.weight < friend.weight && (oCookie == null || cookie.displaySize > oCookie))
										{
											oCookie = cookie;
										}
									}
									if(squaredEuclidean(xDiff, yDiff) < friend.sight.iRadius + sDiff * sDiff / 4)
									{
										if(cookie.weight < friend.weight && (iCookie == null || cookie.displaySize > iCookie))
										{
											iCookie = cookie;
										}
									}
								}
							}
							if(iCookie != null)
							{
								friend.nextCookie = iCookie;
							}
							else if(oCookie != null)
							{
								friend.nextCookie = oCookie;
							}
							else if(friend.wanderAngle == null)
							{
								friend.wanderAngle = 2 * Math.PI * Math.random();
							}
						}
						if(friend.nextCookie != null && friend.nextCookie.weight > 0)
						{
							if(friend.onSprint)
							{
								if(friend.energy <= friend.energyBase * 0.5)
								{
									player.energy += player.energyReg * deltaTime;
									friend.onSprint = false;
								}
								else
								{
									friend.textContent = friend.emoji.regular;
									speedFactor = friend.sprintFactor;
									friend.energy -= friend.energyCon * deltaTime;
									friend.textContent = friend.emoji.excited;
								}
							}
							else
							{
								if(friend.energy >= friend.energyBase)
								{
									speedFactor = friend.sprintFactor;
									friend.energy -= friend.energyCon * deltaTime;
									friend.onSprint = true;
									friend.textContent = friend.emoji.excited;
								}
								else
								{
									friend.energy += friend.energyReg * deltaTime;
								}
							}
							var xDiff = friend.nextCookie.position.x - friend.position.x;
							var yDiff = friend.nextCookie.position.y - friend.position.y;
							friend.wanderAngle = Math.atan2(yDiff, xDiff);
						}
					}
					break;
				case "REINFORCEMENT_LEARNER":
					
					break;
			}
			var speedFactor = 1;
			if(sprintRequired)
			{
				if(friend.energy > 0)
				{
					speedFactor = friend.sprintFactor;
					friend.energy -= friend.energyCon * deltaTime;
					friend.speed = friend.speedBase / (1 + friend.speedScale * Math.log(friend.weight / friend.weightBase));
					friend.onSprint = false;
				}
			}
			friend.position.x += friend.speed * Math.cos(friend.wanderAngle) * deltaTime * speedFactor;
			friend.position.y += friend.speed * Math.sin(friend.wanderAngle) * deltaTime * speedFactor;
			for(i in friends)
			{
				var otherFriend = friends[i];
				if(otherFriend.weight > friend.weight)
				{
					xDiff = friend.position.x - otherFriend.position.x;
					yDiff = friend.position.y - otherFriend.position.y;
					sDiff = otherFriend.displaySize * otherFriend.snapBoundary - friend.displaySize;
					var distance = squaredEuclidean(xDiff, yDiff);
					if(distance <= sDiff * sDiff / 4)
					{
						otherFriend.weight += friend.weight;
						otherFriend.displaySize = Math.sqrt(predator.weight);
						var index = friends.indexOf(friend);
						friend.parentNode.removeChild(friend);
						friends.splice(index, 1);
						return;
					}
				}
			}
			if(friend.position.x < friend.displaySize / 2)
			{
				friend.wanderAngle = Math.PI - friend.wanderAngle;
				friend.position.x = friend.displaySize / 2;
			}
			if(friend.position.x > fieldSizeX - friend.displaySize / 2)
			{
				friend.wanderAngle = Math.PI - friend.wanderAngle;
				friend.position.x = fieldSizeX - friend.displaySize / 2;
			}
			if(friend.position.y < friend.displaySize / 2)
			{
				friend.wanderAngle = 0 - friend.wanderAngle;
				friend.position.y = friend.displaySize / 2;
			}
			if(friend.position.y > fieldSizeY - friend.displaySize / 2)
			{
				friend.wanderAngle = 0 - friend.wanderAngle;
				friend.position.y = fieldSizeY - friend.displaySize / 2;
			}
			if(friend.displaySize > player.displaySize)
			{
				friend.style.zIndex = "110";
			}
			else
			{
				friend.style.zIndex = "90";
			}
			showOnCamera(friend);
		}

		function absoluteMin(a, b)
		{
			if(Math.abs(a) < Math.abs(b))
				return a;
			return b;
		}

		function squaredEuclidean(x, y)
		{
			return x * x + y * y;
		}

	</script>
</html>